﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace Jayden.Dll.BitTorrent
{
	public class BitTorrentFile
	{
		private byte[] m_InfoHash;
		private byte[] m_Datas;
		private Dictionary<string, object> m_MetaInfo;

		public BitTorrentFile(string filepath)
		{
			BEncoding bencoding = new BEncoding();
			m_Datas = File.ReadAllBytes(filepath);
			int offset = 0;
			m_MetaInfo = bencoding.ReadDictionary(m_Datas, ref offset);
			m_InfoHash = bencoding.Hash;
		}

		public override string ToString() { return BitConverter.ToString(m_InfoHash).Replace("-", ""); }
		public override bool Equals(object obj)
		{
			if (obj is BitTorrentFile)
			{
				BitTorrentFile other = obj as BitTorrentFile;
				for (int c = 0; c < m_InfoHash.Length; c++)
					if (m_InfoHash[c] != other.m_InfoHash[c])
						return false;
				return true;
			}
			return false;
		}
		public override int GetHashCode()
		{
			int hash = 0;
			for (int c = 0; c < m_InfoHash.Length; c++)
			{
				hash *= 31;
				hash += m_InfoHash[c];
			}
			return hash;
		}

		protected object GetObjectEntry(Dictionary<string, object> dic, string name)
		{
			object value = null;
			if (!dic.TryGetValue(name, out value))
				value = null;
			return value;
		}
		protected string GetString(byte[] bytes)
		{
			return Encoding.GetString(bytes as byte[]);
		}
		protected string GetStringEntry(Dictionary<string, object> dic, string name)
		{
			object value = GetObjectEntry(dic, name);
			if (value != null)
				return GetString(value as byte[]);
			return null;
		}

		public byte[] InfoHash { get { return m_InfoHash; } }

		public Dictionary<string, object> MetaInfo { get { return m_MetaInfo; } }
		public Dictionary<string, object> Info { get { return GetObjectEntry(MetaInfo, "info") as Dictionary<string, object>; } }
		public string Announce { get { return GetStringEntry(MetaInfo, "announce"); } }
		public List<List<string>> AnnounceList
		{
			get
			{
				List<object> listOfLists = GetObjectEntry(MetaInfo, "announce-list") as List<object>;
				if (listOfLists == null)
					return null;
				List<List<string>> listOfListsOfStrings = new List<List<string>>();
				int index = 0;
				foreach (object obj in listOfLists)
				{
					List<object> listOfStrings = obj as List<object>;
					listOfListsOfStrings.Add(new List<string>());
					foreach (object objStr in listOfStrings)
						listOfListsOfStrings[index].Add(GetString(objStr as byte[]));
					index++;
				}
				return listOfListsOfStrings;
			}
		}
		public Encoding Encoding
		{
			get
			{
				string encName = GetStringEntry(MetaInfo, "encoding");
				if(string.IsNullOrWhiteSpace(encName))
					return Encoding.UTF8;
				return Encoding.GetEncoding(encName);
			}
		}

		public int PieceLength
		{
			get
			{
				long pieceLength = (long)GetObjectEntry(Info, "piece length");
				return (int)pieceLength;
			}
		}
		public int PiecesCount { get { return Pieces.Length / 20; } }
		public byte[] Pieces
		{
			get
			{
				return GetObjectEntry(Info, "pieces") as byte[];
			}
		}
		public bool Private
		{
			get
			{
				object privateObj = GetObjectEntry(Info, "private");
				if (privateObj == null)
					return false;
				return ((long)privateObj) == 1;
			}
		}

		public string Name { get { return GetStringEntry(Info, "name"); } }

		public bool IsSingleFileMode { get { return !IsMultipleFileMode; } }
		public bool IsMultipleFileMode { get { return GetObjectEntry(Info, "files") != null; } }

		public long Length { get { return (long)GetObjectEntry(Info, "length"); } }

		public List<Dictionary<string, object>> Files
		{
			get
			{
				if (IsSingleFileMode)
					return null;
				List<object> list = GetObjectEntry(Info, "files") as List<object>;
				if (list == null)
					return null;
				List<Dictionary<string, object>> result = new List<Dictionary<string,object>>();
				foreach(object obj in list)
					result.Add(obj as Dictionary<string, object>);
				return result;
			}
		}

		public int MultipleFilesCount { get { return IsSingleFileMode ? 0 : (GetObjectEntry(Info, "files") as List<object>).Count; } }
		public long GetFileLength(int fileIndex) { return (long)Files[fileIndex]["length"]; }
		public string GetFilePath(int fileIndex)
		{
			List<string> paths = new List<string>();
			List<object> path = Files[fileIndex]["path"] as List<object>;
			foreach (object obj in path)
				paths.Add(GetString(obj as byte[]));
			return string.Join("\\", paths);
		}

		public int BitFieldLength { get { return (int)Math.Ceiling(Pieces.Length / 160.0); } }
		public int BitFieldMessageLength { get { return BitFieldLength + 1; } }
		public int MaxMessageLength { get { return Math.Max(BitFieldMessageLength, 16 * 1024 + 9); } }
	}
}
